#include "Debug.h"
#ifdef DEBUGGING
Color DebugTracer::TraceRay( const Ray& ray, int depth) const
{
    ContactData data;
    //if we are a shadow ray, we dont need the closest contact!
    if(depth == 10)
    {
        data = m_scene->CheckContact_Debug(ray, false);
        if(!data.m_hit)
            OUT_STRING("Shadow Miss! ");
    }
    else
    {
        data = m_scene->CheckContact_Debug(ray);
    }

    // if this ray hit something
    //10 == shadow ray, so as an early out return the 10.5f code
    if(depth ==10 && data.m_hit )
    {
        OUT_STRING("Shadow Hit! ");
        OUT_STRING(data.m_hitTarget->m_information);
        Color c;
        c.m_a = 10.5f;
        return c;
    }
    //Else, we are either a reflection ray or a source
    if (data.m_hit && depth <= 2)   
    {
        OUT_STRING("Primary Hit...");
        Color RETURN_COLOR;
        //check if this is in shadow
        Ray shadow;
        shadow.m_origin = data.m_hitPoint;
        float shade = 1.0f;
        OUT_STRING("Managing Shadows...");
        for( unsigned int i = 0; i < m_scene->GetLights().size(); ++i)
        {
            Vec3 lightDir;
            if(m_scene->GetLights()[i]->IsDirectional())
                lightDir = m_scene->GetLights()[i]->GetDirection();
            else
            {
                Vec3 toLight = shadow.m_origin - m_scene->GetLights()[i]->GetPosition();
                toLight= toLight.normalise();
            }
            shadow.m_dir = lightDir.GetInverted();
            shadow.m_hitObject = data.m_hitTarget;
            Color Shadow_Test = TraceRay(shadow, 10);
            if(Shadow_Test.m_a == 10.5f)
                shade -= 0.8f*1.0f/(float)m_scene->GetLights().size();
        }

        OUT_STRING("Managing Reflections...");
        //check if we can reflect something
        if(data.m_hitTarget->Reflect())
        {
            Ray reflection; 
            reflection.m_origin = data.m_hitPoint;
            reflection.m_dir = ray.m_dir.Reflect(data.m_hitNormal);
            reflection.m_dir.normalise();
            reflection.m_hitObject = data.m_hitTarget;
            Color Reflection_Color = TraceRay(reflection, depth+1);        
            // if we do not hit anything with the reflection ray, use the old contact data
            // for the original hit
            if(Reflection_Color.m_a == 1.5f)
            {
                OUT_STRING("Reflection miss...")
                RETURN_COLOR += ShadeThis(data) * shade;
            }
            else //our color has been calculated depending on what we hit with the reflection vector, so return the combo
            {
                OUT_STRING("Reflection hit...");
                OUT_STRING(data.m_hitTarget->m_information);
                RETURN_COLOR += (ShadeThis(data) + 
                    (Reflection_Color *data.m_hitTarget->GetReflectance())) * 0.5f * shade;
            }
        }
        if(data.m_hitTarget->IsTransparent())
        {
            OUT_STRING("Managing Refraction...");
            //if we have a refratic index, and refract returns true,
            if( data.m_hitTarget->GetRefractiveIndex() > 0.1f)
            {   
                Ray refracted = ray;
                //if refract fails, just use the old ray direction
                refracted.m_dir.Refract(data.m_hitNormal, data.m_hitTarget->GetRefractiveIndex());
                refracted.m_origin = data.m_hitPoint;
                refracted.m_hitObject = data.m_hitTarget;
                Color Refracted_Color = TraceRay(refracted, depth+1);
                if(Refracted_Color.m_a == 1.5f)
                {
                    OUT_STRING("Refraction miss");
                    RETURN_COLOR += ShadeThis(data) * data.m_hitTarget->GetOpacity() * shade;
                }
                else //our color has been calculated depending on what we hit with the REFRACTION vector, so return the combo
                {
                    OUT_STRING("Refraction hit");
                    OUT_STRING(data.m_hitTarget->m_information);
                    RETURN_COLOR +=  (ShadeThis(data) + 
                        (Refracted_Color)) * 0.5f * shade;
                }
            }
        }
        if(!data.m_hitTarget->Reflect() && !data.m_hitTarget->IsTransparent()) //if this is not reflective or transparent
        {
            RETURN_COLOR +=  ShadeThis(data) * shade;
        }
        return RETURN_COLOR;
    }
    else if(depth == 0)
    {
        return m_scene->m_clearColor;
    }
    else
    {
        OUT_STRING("Returning Clear Color");
        Color c = m_scene->m_clearColor;
        c.m_a = 1.5f;
        return c;
    }
}
Color DebugTracer::ShadeThis( const ContactData& d ) const
{
    OUT_STRING("Shading Pixel...");
    OUT_STRING(d.m_hitTarget->m_information);

    //this is the color to return
    Vec3 pos = d.m_hitPoint;
    Color col = d.m_hitTarget->GetColor();
    if(d.HasColorStorred())
        col = d.GetColor();

    std::vector<Light*>& lights = m_scene->GetLights();
    Vec3 normal = d.m_hitNormal;
    normal.normalise();
    Color spec, diffuse, ambient;
    for(unsigned int i = 0; i < lights.size(); ++i)
    {
        Vec3 toLight;
        float fOff = 1.0f;
        if(lights[i]->IsDirectional())
            toLight = lights[i]->GetDirection().GetInverted();
        else
        {
            Vec3 toLight = pos - lights[i]->GetPosition();
            toLight= toLight.normalise();
            fOff = lights[i]->GetFallOff();
        }
        // Determine diffuse light intensity that strikes the vertex
        float s = std::max(normal.DotProduct(toLight), 0.0f) * fOff;

        //determine the specular intensity
        float t = 0.0f;
        if(d.m_hitTarget->UseSpec())
        {
            // Compute reflection vector
            Vec3 r =toLight.Reflect(normal);
            // Compute the vector from vertex to eye position
            Vec3 toEye = (pos-m_scene->m_camera->m_pos).normalise();
            // Determine how much specular light makes it to the eye
            t = pow(std::max(r.DotProduct(toEye), 0.000001f), lights[i]->GetExponent()) * fOff;
        }
        const Material& m = d.m_hitTarget->GetMaterial();
        spec += (lights[i]->GetSpecular() * t) * m.GetSpecular();
        diffuse += (col * lights[i]->GetDiffuse()) * s * m.GetDiffuse();
        ambient += (col * lights[i]->GetAmbient()) * m.GetAmbient();
    }
    // Sum of them all together
    return (ambient + diffuse + spec) *= 0.5f;
}
#endif
